import { Mini3d, Resource } from "mini3d";
import * as THREE from "three";
import { OutputPass } from "three/examples/jsm/postprocessing/OutputPass.js";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import GUI from "three/examples/jsm/libs/lil-gui.module.min.js";
import { UnrealBloomPass } from "./UnrealBloomPass";
import { EmissivePass } from "./EmissivePass";
import { MixPass } from "./MixPass";
// import { DemoPass } from "./DemoPass";

const assets = [
  { name: "demo", type: "GLTF", path: "/demo.gltf" },
  { name: "demo2", type: "GLTF", path: "/demo2.gltf" },
];

export class AppScene extends Mini3d {
  resource = new Resource();
  gui = new GUI();
  emissiveEffect?: EffectComposer;

  constructor(canvas: HTMLCanvasElement) {
    super(canvas);

    this.renderer.instance.outputColorSpace = THREE.SRGBColorSpace;
    const directLight = new THREE.DirectionalLight(0xffffff, 1);
    const ambientLight = new THREE.AmbientLight(0xa8a4a4, 1);

    this.scene.add(directLight, ambientLight);

    this.initEffect();
    this.resource.loadAll(assets).then(() => {
      this.init();
    });
  }

  initEffect() {
    const emissiveEffect = new EffectComposer(this.renderer.instance);
    this.emissiveEffect = emissiveEffect;
    emissiveEffect.renderToScreen = false;
    emissiveEffect.addPass(new EmissivePass(this.scene, this.camera.instance));
    const unrealBloomPass = new UnrealBloomPass(
      new THREE.Vector2(
        this.sizes.width * this.sizes.pixelRatio,
        this.sizes.height * this.sizes.pixelRatio
      ),
      0.2,
      0.05
    );
    emissiveEffect.addPass(unrealBloomPass);
    const loomGui = this.gui.addFolder("unrealBloomPass");
    loomGui.add(unrealBloomPass, "strength", 0, 10);
    loomGui.add(unrealBloomPass, "radius", 0, 10);

    // 最终effect
    const finalEffect = new EffectComposer(this.renderer.instance);
    finalEffect.addPass(new RenderPass(this.scene, this.camera.instance));

    // 混合其他纹理
    const mixPass = new MixPass({
      texture: emissiveEffect.renderTarget2.texture,
    });
    finalEffect.addPass(mixPass);

    finalEffect.addPass(new OutputPass());

    this.renderer.composer = finalEffect;
    this.renderer.postprocessing = true;
  }

  update(deltaTime: number) {
    if (this.emissiveEffect) {
      this.emissiveEffect.render();
    }
    super.update(deltaTime);
  }

  init() {
    const demo = this.resource.getResource("demo2");
    const demoScene = demo.scene as THREE.Object3D;
    this.scene.add(demoScene);
    console.log(demoScene);

    const emissiveObjects: THREE.Mesh<
      THREE.BufferGeometry,
      THREE.MeshStandardMaterial
    >[] = [];
    demoScene.traverse((item) => {
      if (
        item instanceof THREE.Mesh &&
        item.material instanceof THREE.MeshStandardMaterial
      ) {
        if (
          item.material.emissive.getHex() !== 0x000000 &&
          item.material.emissiveIntensity
        ) {
          emissiveObjects.push(item);
          // item.material.emissiveIntensity = 1.0;
          // item.material.emissive.setHex(0xffffff);
        }
      }
    });

    const tickFns: ((elapsed: number) => void)[] = [];
    if (emissiveObjects.length) {
      emissiveObjects.forEach((item) => {
        const material = item.material as THREE.MeshStandardMaterial;
        console.log("material.onBeforeCompile", material.onBeforeCompile);

        material.onBeforeCompile = (shader) => {
          shader.uniforms = {
            ...shader.uniforms,
            uTime: { value: 0.0 },
          };

          tickFns.push((elapsed: number) => {
            shader.uniforms.uTime.value = elapsed;
          });

          shader.vertexShader = shader.vertexShader.replace(
            /^/,
            `#define USE_UV\n`
          );
          shader.fragmentShader = shader.fragmentShader.replace(
            /^/,
            `#define USE_UV\n`
          );
          shader.fragmentShader = shader.fragmentShader.replace(
            "uniform vec3 emissive;",
            `
            uniform vec3 emissive;
            uniform float uTime;
          `
          );
          shader.fragmentShader = shader.fragmentShader.replace(
            "vec3 totalEmissiveRadiance = emissive;",
            `
            // vec3 totalEmissiveRadiance = emissive * sin(uTime * 0.001);
            // vec3 totalEmissiveRadiance = emissive * sin(uTime * vUv.x * 0.01) * vUv.x;
            // vec3 totalEmissiveRadiance = emissive * sin(uTime * 0.004 + vUv.x * 10.0) * vUv.x;
            vec3 totalEmissiveRadiance = emissive * max(0.0, (sin(uTime * 0.004 + vUv.x * 10.0) * vUv.x));
            // vec3 totalEmissiveRadiance = vec3(0.0, 0.0, 0.0);
          `
          );

          // console.log("shader.vertexShader", shader.vertexShader);
          // console.log("shader.fragmentShader", shader.fragmentShader);
        };
      });
      this.time.on("tick", (deltaTime) => {
        // console.log("tick", this.time.elapsed);
        if (tickFns.length) {
          for (let index = 0; index < tickFns.length; index++) {
            const fn = tickFns[index];
            fn.call(null, this.time.elapsed);
          }
        }
      });
    }
  }
}
